<?php
namespace Ayhome\Suite\Command;

use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option;
use think\console\Output;
use think\facade\Config;
use think\facade\Env;
use think\Db;

use FastD\Swoole\Process;
use swoole_process;
class Task extends Command
{
  protected $config = [];
  public function configure()
  {
    $this->setName('task')
        ->addArgument('action', Argument::OPTIONAL, "start|stop|restart|crontab|check", 'start')
        ->addOption('daemon', 'd', Option::VALUE_NONE, 'Run the task in daemon mode.')
        ->setDescription('task process for suite');
  }

  public function execute(Input $input, Output $output)
  {
    $action = $input->getArgument('action');
      $this->config = config('crontab.');
    if (!$this->config) {
      $this->config = config('task.');
    }

    if ($this->input->hasOption('daemon')) {
      $this->config['daemonize'] = true;
    }

    $run_name = $this->config['run_name'];
    if (!$run_name) {
      $run_name = 'task';
      $this->config['run_name'] = $run_name;
    }

    $logs_path = $this->config['logs_path'];
    if (!$logs_path) {
      $logs_path = Env::get('runtime_path').'log/';
    }
    $this->config['logs_path'] = $logs_path;

    if (empty($this->config['pid_file'])) {
      $this->config['pid_file'] = Env::get('runtime_path') .'suite-task.pid';
    }

    if (in_array($action, ['start', 'stop','restart','check','crontab'])) {
        $this->$action();
    } else {
        $output->writeln("<error>Invalid argument action:{$action}, Expected start|stop|restart|check .</error>");
    }
  }

  public function start()
  {

    $pidFile = $this->config['pid_file'];

    if (file_exists($pidFile)) {
      $this->output->writeln('<error>suite task process is already running.</error>');
      return false;
    }

    if ($this->config['database']) {
      $tasks = db($this->config['database'])->select();
      $this->config['tasks'] = $tasks;
    }


    
    $crontExpre = new \Ayhome\Suite\Parser\Crontab();
    $run_name = $this->config['run_name'];
    $task = new Process('suite:'.$run_name, function () use($crontExpre) {
      timer_tick(1000, function ($id) use($crontExpre) {

        $tasks = $this->config['tasks'];
        foreach ($tasks as $idx => $k) {
          if (!$k['cron']) continue;
          $sec = date('s');

          $r = $crontExpre::parse($k['cron']);
          if (!isset($r[$sec])) continue;

          $this->runTask($k);
          $time = time();
          
          $k['utime'] = $time;
          $this->log($k,$idx);

          if ($this->config['database']) {
            $u['utime'] = $time;
            Db::name($this->config['database'])
                      ->where('id',$k['id'])
                      ->update($u);
          }
          
        }

      });
    });

    if ($this->config['daemonize']) {
      $task->daemon();
    }
    
    $pid = $task->start();
    file_put_contents($pidFile, $pid);

    $task->wait(function ($ret) {
      unlink($pidFile);
    });
    return;
  }


  public function restart()
  {
    $this->stop();
    $this->start();
  }

  public function stop()
  {
    $pidFile = $this->config['pid_file'];
    if (is_file($pidFile)) {
      $pid = (int) file_get_contents($pidFile);
      swoole_process::kill($pid, SIGTERM);
      unlink($pidFile);
    } else {
      $pid = 0;
    }
    return;
  }

  public function runTask($cron ='')
  {
    if ($cron['type'] == 'remote' && $cron['url']) {
      $uri = parse_url($cron['url']);
      $host = $uri['host'];
      $path = $uri['path'];

      $client = new \GuzzleHttp\Client();
      $res = $client->request('GET', $cron['url']);
    }elseif ($cron['type'] == 'class') {
      $cls_arr = explode("/", $cron['url']);
      if (count($cls_arr) == 4) {
        $ac = $cls_arr[3];
        $event = \think\facade\App::controller($cls_arr[0].'/'.$cls_arr[2], $cls_arr[1]);
        $event->$ac();
      }
    }
  }

  public function crontab()
  {
    $cmd = 'crontab -l';
    $ret = shell_exec("$cmd");
    $arr =  explode("\n", $ret);

    $path = Env::get('root_path') ;
    $cmds = 'think task check';

    $isCrontab = true;
    foreach ($arr as $k) {
      if (!$k) continue;

      //如果存在
      if (strstr($k,$path) && strstr($k,$cmds)) {
        echo "this Command is \n".$k."\n";
        $isCrontab = false;
      }
    }


    if ($isCrontab) {
      $ccamd = '* * * * *  '.PHP_BINDIR ."/php {$path}think task check";
      $ret = shell_exec("crontab -l > task.cron");
      $ret = shell_exec("echo '{$ccamd}' >> task.cron");
      $ret = shell_exec("crontab task.cron");
      unlink('./task.cron');
    }



  }

  public function check()
  {
    $run_name = $this->config['run_name'];
    //检查服务
    $cmd = 'ps axu|grep "suite:'.$run_name.'"|grep -v "grep"|wc -l';
    $ret = shell_exec("$cmd");
    $ret = rtrim($ret, "\r\n");

    if ($ret === "0") {
      $log_path = Env::get('root_path') . 'task.logs';
      $path = Env::get('root_path') . 'index.php';
      $php_path = PHP_BINDIR;
      $php_path .= "/php";
      $start_master_cmd = $php_path . " think task restart -d >> " . $log_path . " 2>&1 &";
      print_r($start_master_cmd);
      $r = shell_exec("$start_master_cmd");
    }else{
      $cmd = 'ps axu|grep "suite:'.$run_name.'"|grep -v "grep"';
      $ret = shell_exec("$cmd");
      print_r($ret);
    }
    exit();
  }

  public function log($msg = '',$id = 0)
  {
    if (is_array($msg)) {
      $msg = json_encode($msg);
    }
    $logs_path = $this->config['logs_path']."task_$id.log";
    file_put_contents($logs_path, $msg."\n",FILE_APPEND);
  }

}
